#include "UniversalMaster.hpp"
#include "Comms/SerialPortManager/SerialPortManager.hpp"
#include "MiscUtils/TdrMisc/SEngine.h"

using namespace std;

std::vector<UniversalMaster*> UniversalMaster::allMasters;

UniversalMaster::UniversalMaster() {
	callbackIndex = addMeToArray(this);
}

UniversalMaster::~UniversalMaster() {
	removeMeFromArray(callbackIndex);
}

int UniversalMaster::init(int simuMode) {
	_flags.simuMode = simuMode;
	if (_flags.simuMode > 0) {
		return 1;
	}

	SerialPortManager::HandShakeProtocolSerial prot;
	int identify = int(_meta.identifier.length() > 0);
	int handShakeNum = 1 + identify;
	prot.handshakes.resize(handShakeNum);
	{
		HandShakeProtocol::OneShake& shake = prot.handshakes[0];
		shake.toSend.dataToSend = TFormater::str2u8("who\r\n");
		shake.exp.resize(1);
		shake.exp.isReplyTemplate[0] = false;
		shake.exp.possibleReplies[0] = TFormater::str2u8(_meta.model + "\r\n");
		shake.exp.timeout = SLAVE_TIME_OUT_MS;
	}
	if (identify) {
		HandShakeProtocol::OneShake& shake = prot.handshakes[1];
		shake.toSend.dataToSend = TFormater::str2u8("which\r\n");
		shake.exp.resize(1);
		shake.exp.isReplyTemplate[0] = false;
		shake.exp.possibleReplies[0] = TFormater::str2u8(_meta.identifier + "\r\n");
		shake.exp.timeout = SLAVE_TIME_OUT_MS;
	}

	TLogger::writeLine("To connect " + _meta.model + ":" + _meta.identifier);
	SerialPortManager* port = new SerialPortManager();

	port->_callback.referenceIndex = callbackIndex;
	port->_callback.sentCallbackMP = serialSentCBWrap;
	port->_callback.receiveCallbackMP = serialReceiveCBWrap;

	wstring hwid = TFormater::string2wstring(_meta.appearance);
	int ret = port->connectHWIDWithHandshake(hwid.c_str(), 0, prot, 115200, 'n', 8, 1);

	if (ret > 0) {
		TLogger::writeLine("Connected");

		_misc.slave = port;
	}
	else {
		TLogger::writeLine("Connect failed.");
		delete port;
	}

	return ret;
}

int UniversalMaster::issueCommand(std::string command, std::string paras) {
	if (_flags.simuMode) {
		return 1;
	}
	if (_misc.slave == nullptr) {
		if (_callbacks.sentCB != NULL) {
			_callbacks.sentCB(_misc.nameInLog, _meta.referenceName, "SimuSent", TFormater::str2u8(command));
		}
		return 0;
	}
	SerialPortManager& comm = *((SerialPortManager*)_misc.slave);
	string tosend = command + paras;
	if (tosend[tosend.length() - 1] != '\n') {
		tosend += "\n";
	}
	int ret = comm.send(tosend);
	if (ret == 0) {
		ret = -1;
	}

	return ret;
}

int UniversalMaster::comfirmReply(std::string reply, int timeout) {
	if (_flags.simuMode) {
		return 1;
	}
	if (_misc.slave == nullptr) {
		if (_callbacks.replyCB != NULL) {
			_callbacks.replyCB(_misc.nameInLog, _meta.referenceName, "SimuRcv", TFormater::str2u8(reply), vector<uint8_t>());
		}
		return 0;
	}
	SerialPortManager& comm = *((SerialPortManager*)_misc.slave);

	if (reply.length() == 0) {
		reply = "\r\n";
	}
	int ret = comm.checkReplyStr(reply, 0);
	if (ret == 0) {
		ret = -1;
	}

	return ret;
}

//int UniversalMaster::comfirmAnyReply(int timeout) {
//	if (_flags.simuMode) {
//		return 1;
//	}
//
//}

int UniversalMaster::flush() {
	if (_flags.simuMode) {
		return 1;
	}
	if (_misc.slave == nullptr) {
		return 0;
	}
}

void UniversalMaster::serialSentCB(bool success, const void* log, int logLen, const void* data, int dataLen) {
	vector<uint8_t> logV(logLen);
	memcpy(logV.data(), log, logLen);

	if (_callbacks.flagEnable) {
		if (_callbacks.sentCB != NULL) {
			_callbacks.sentCB(_misc.nameInLog, _meta.referenceName, "Sd", logV);
		}
	}
}

void UniversalMaster::serialReceiveCB(bool success, const void* log, int logLen, const void* data, int dataLen) {
	vector<uint8_t> logV(logLen), datV(dataLen);
	if (logLen > 0) {
		memcpy(logV.data(), log, logLen);
	}
	if (dataLen > 0) {
		memcpy(datV.data(), data, dataLen);
	}

	if (_callbacks.flagEnable) {
		if (success) {
			if (_callbacks.replyCB != NULL) {
				_callbacks.replyCB(_misc.nameInLog, _meta.referenceName, "Rcv", logV, datV);
			}
		}
		else {
			if (_callbacks.timeoutCB != NULL) {
				_callbacks.timeoutCB(_misc.nameInLog, _meta.referenceName, "T.O.", logV, datV);
			}
		}
	}
}

void UniversalMaster::addToAllBuffer(const std::string& in) {
	_callbacks.allCBBuffer += in;
}

void UniversalMaster::serialSentCBWrap(int index, bool success, const void* log, int logLen, const void* data, int dataLen) {
	allMasters[index]->serialSentCB(success, log, logLen, data, dataLen);
}

void UniversalMaster::serialReceiveCBWrap(int index, bool success, const void* log, int logLen, const void* data, int dataLen) {
	allMasters[index]->serialReceiveCB(success, log, logLen, data, dataLen);
}

int UniversalMaster::addMeToArray(UniversalMaster* myPtr) {
	for (int idx = 0; idx < allMasters.size(); idx++) {
		if (allMasters[idx] == nullptr) {
			allMasters[idx] = myPtr;
			return idx;
		}
	}
	allMasters.push_back(myPtr);
	return allMasters.size() - 1;
}

void UniversalMaster::removeMeFromArray(int index) {
	allMasters[index] = nullptr;
}